﻿using System.Collections.Generic;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Mathematics;
using Microsoft.Xna.Framework.Graphics;
using Rectangle = Microsoft.Xna.Framework.Rectangle;
using XNAVector2 = Microsoft.Xna.Framework.Vector2;

namespace CymbergajTouch.Match
{
    /// <summary>
    /// Class that represents ice rink with its boards. It produces goal events.
    /// </summary>
    class IceRink
    {
        //Size
        public const int iceWidth = 1280;
        public const int iceHeight = 800;

        //Boards
        private Geom topBoard;
        private Geom bottomBoard;
        private Geom topLeftBoard;
        private Geom bottomLeftBoard;
        private Geom topRightBoard;
        private Geom bottomRightBoard;
        private const int boardWidth = 10000;
        private const int goalFieldWidth = 60;

        //Blocking areas for collisions with pushers
        private Geom leftBlockingIce;
        private Geom rightBlockingIce;
        private Geom leftBlockingGoal;
        private Geom rightBlockingGoal;

        //Goal event
        public event GoalEventHandler GoalScored;

        //Texture
        private Texture2D iceTexture;
        private Texture2D goalTexture;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="physicsSimulator">Farseer physics simulator</param>
        public IceRink(PhysicsSimulator physicsSimulator)
        {
            CreateBoards(physicsSimulator);
            CreateBlockingAreas(physicsSimulator);
        }

        /// <summary>
        /// Creates ice rink boards.
        /// </summary>
        /// <param name="physicsSimulator">Farseer physics simulator</param>
        private void CreateBoards(PhysicsSimulator physicsSimulator)
        {
            List<Geom> boards = new List<Geom>();
            float boardMass = 1.0f;

            int horizontalBoardWidth = iceWidth * 10;

            Body horizontalBoard = BodyFactory.Instance.CreateRectangleBody(horizontalBoardWidth, boardWidth, boardMass);

            topBoard = GeomFactory.Instance.CreateRectangleGeom(horizontalBoard, horizontalBoardWidth, boardWidth);
            topBoard.Body.Position = new Vector2(iceWidth / 2, -boardWidth / 2);
            boards.Add(topBoard);

            bottomBoard = new Geom(new Body(horizontalBoard), topBoard);
            bottomBoard.Body.Position = new Vector2(iceWidth / 2, iceHeight + boardWidth / 2);
            boards.Add(bottomBoard);


            Body verticalBoard = BodyFactory.Instance.CreateRectangleBody(boardWidth, iceHeight / 2, boardMass);

            topLeftBoard = GeomFactory.Instance.CreateRectangleGeom(verticalBoard, boardWidth, iceHeight / 2);
            topLeftBoard.Body.Position = new Vector2(-boardWidth / 2, iceHeight / 4);
            boards.Add(topLeftBoard);

            bottomLeftBoard = new Geom(new Body(verticalBoard), topLeftBoard);
            bottomLeftBoard.Body.Position = new Vector2(-boardWidth / 2, 3 * iceHeight / 4);
            boards.Add(bottomLeftBoard);

            topRightBoard = new Geom(new Body(verticalBoard), topLeftBoard);
            topRightBoard.Body.Position = new Vector2(iceWidth + boardWidth / 2, iceHeight / 4);
            boards.Add(topRightBoard);

            bottomRightBoard = new Geom(new Body(verticalBoard), topLeftBoard);
            bottomRightBoard.Body.Position = new Vector2(iceWidth + boardWidth / 2, 3 * iceHeight / 4);
            boards.Add(bottomRightBoard);

            foreach (Geom board in boards)
            {
                board.Body.IsStatic = true;
                physicsSimulator.Add(board);
            }

            ResizeGoal(0.33f, true);
            ResizeGoal(0.33f, false);
        }

        /// <summary>
        /// Creates blocking areas that prevent from pusher being hit to opponent area.
        /// </summary>
        /// <param name="physicsSimulator">Farseer physics simulator</param>
        private void CreateBlockingAreas(PhysicsSimulator physicsSimulator)
        {
            float boardMass = 1.0f;

            Body blockingIce = BodyFactory.Instance.CreateRectangleBody(boardWidth, boardWidth, boardMass);

            leftBlockingIce = GeomFactory.Instance.CreateRectangleGeom(blockingIce, boardWidth, boardWidth);
            leftBlockingIce.Body.Position = new Vector2(-boardWidth / 2 + iceWidth / 2, iceHeight / 2);
            leftBlockingIce.Body.IsStatic = true;
            physicsSimulator.Add(leftBlockingIce);

            rightBlockingIce = new Geom(new Body(blockingIce), leftBlockingIce);
            rightBlockingIce.Body.Position = new Vector2(boardWidth / 2 + iceWidth / 2, iceHeight / 2);
            rightBlockingIce.Body.IsStatic = true;
            physicsSimulator.Add(rightBlockingIce);

            Body blockingGoal = BodyFactory.Instance.CreateRectangleBody(boardWidth, iceHeight, boardMass);

            leftBlockingGoal = GeomFactory.Instance.CreateRectangleGeom(blockingGoal, boardWidth, iceHeight);
            leftBlockingGoal.Body.Position = new Vector2(-boardWidth / 2, iceHeight / 2);
            leftBlockingGoal.Body.IsStatic = true;
            physicsSimulator.Add(leftBlockingGoal);

            rightBlockingGoal = new Geom(new Body(blockingGoal), leftBlockingGoal);
            rightBlockingGoal.Body.Position = new Vector2(boardWidth / 2 + iceWidth, iceHeight / 2);
            rightBlockingGoal.Body.IsStatic = true;
            physicsSimulator.Add(rightBlockingGoal);
        }

        /// <summary>
        /// Organizes collision categories between blocking areas, pushers and puck.
        /// </summary>
        /// <param name="leftPusher">left pusher</param>
        /// <param name="rightPusher">right pusher</param>
        /// <param name="puck">puck</param>
        public void SetCollisionCategories(Pusher leftPusher, Pusher rightPusher, Puck puck)
        {
            leftPusher.geom.CollisionCategories = CollisionCategory.Cat1;
            rightPusher.geom.CollisionCategories = CollisionCategory.Cat2;
            puck.geom.CollisionCategories = CollisionCategory.Cat3;
            leftBlockingIce.CollisionCategories = CollisionCategory.Cat4;
            rightBlockingIce.CollisionCategories = CollisionCategory.Cat5;
            leftBlockingGoal.CollisionCategories = CollisionCategory.Cat6;
            rightBlockingGoal.CollisionCategories = CollisionCategory.Cat7;

            leftPusher.geom.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat4;
            rightPusher.geom.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat5;
            puck.geom.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat4 & ~CollisionCategory.Cat5
                & ~CollisionCategory.Cat6 & ~CollisionCategory.Cat7;
            leftBlockingIce.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat1 & ~CollisionCategory.Cat3;
            rightBlockingIce.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat2 & ~CollisionCategory.Cat3;
            leftBlockingGoal.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat3;
            rightBlockingGoal.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat3;
        }

        /// <summary>
        /// Organizes collision categories for extra pusher.
        /// </summary>
        /// <param name="extraPusher">extra pusher</param>
        public void SetExtraPusherCollisionCategory(Pusher extraPusher)
        {
            extraPusher.geom.CollisionCategories = CollisionCategory.Cat8;
            if (extraPusher.left)
            {
                extraPusher.geom.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat4;
                leftBlockingIce.CollidesWith = leftBlockingIce.CollidesWith & ~CollisionCategory.Cat8;
                rightBlockingIce.CollidesWith = rightBlockingIce.CollidesWith & CollisionCategory.Cat8;
            }
            else
            {
                extraPusher.geom.CollidesWith = CollisionCategory.All & ~CollisionCategory.Cat5;
                rightBlockingIce.CollidesWith = rightBlockingIce.CollidesWith & ~CollisionCategory.Cat8;
                leftBlockingIce.CollidesWith = leftBlockingIce.CollidesWith & CollisionCategory.Cat8;
            }
        }

        /// <summary>
        /// Sets ice texture.
        /// </summary>
        /// <param name="iceTexture">ice texture</param>
        public void SetIceTexture(Texture2D iceTexture)
        {
            this.iceTexture = iceTexture;
        }


        /// <summary>
        /// Sets goal texture.
        /// </summary>
        /// <param name="goalTexture">goal texture</param>
        public void SetGoalTexture(Texture2D goalTexture)
        {
            this.goalTexture = goalTexture;
        }

        /// <summary>
        /// Renders ice rink objects
        /// </summary>
        /// <param name="spriteBatch">sprite batch</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            //ice
            Rectangle destinationRectangle = new Rectangle(0, 0, iceWidth, iceHeight);
            spriteBatch.Draw(iceTexture, destinationRectangle, Color.White);

            //goals
            int leftVerticalBoardHeight = (int)(topLeftBoard.Body.Position.Y + topLeftBoard.AABB.Height / 2);

            Rectangle leftGoalRectangle = new Rectangle(0, leftVerticalBoardHeight, goalFieldWidth, (int)(iceHeight - 2 * leftVerticalBoardHeight));
            spriteBatch.Draw(goalTexture, leftGoalRectangle, new Color(Color.White, 200));

            int rightVerticalBoardHeight = (int)(topRightBoard.Body.Position.Y + topRightBoard.AABB.Height / 2);
            Rectangle rightGoalRectangle = new Rectangle(iceWidth - goalFieldWidth, rightVerticalBoardHeight, goalFieldWidth, (int)(iceHeight - 2 * rightVerticalBoardHeight));
            spriteBatch.Draw(goalTexture, rightGoalRectangle, null, new Color(Color.White, 200), 0.0f, new XNAVector2(0, 0), SpriteEffects.FlipHorizontally, 0);
        }

        /// <summary>
        /// Sets new goal size.
        /// </summary>
        /// <param name="newSize">new goal size expressed as a part of ice height (from 0 to 1)</param>
        /// <param name="left">left or right goal</param>
        public void ResizeGoal(float newSize, bool left)
        {
            Geom topVerticalBoard;
            Geom bottomVerticalBoard;
            if (left)
            {
                topVerticalBoard = topLeftBoard;
                bottomVerticalBoard = bottomLeftBoard;
            }
            else
            {
                topVerticalBoard = topRightBoard;
                bottomVerticalBoard = bottomRightBoard;
            }

            topVerticalBoard.Body.Position = new Vector2(topVerticalBoard.Body.Position.X,
                iceHeight / 4 - newSize * 0.5f * iceHeight);
            bottomVerticalBoard.Body.Position = new Vector2(bottomVerticalBoard.Body.Position.X,
                3 * iceHeight / 4 + newSize * 0.5f * iceHeight);
        }

        /// <summary>
        /// Checks if puck is in the back of the net.
        /// </summary>
        public void CheckForGoals(Vector2 puckPosition)
        {
            if (puckPosition.X <= 0)
            {
                GoalScored(this, true);
            }
            else if (puckPosition.X >= iceWidth)
            {
                GoalScored(this, false);
            }
        }

    }
}
